-module(camera).
-export([get_ray/2]).
-export([new/0, new/4]).
-export([get_render_size/1, set_render_size/2]).
-export([get_fov/1, set_fov/2]).
-export([get_position/1, set_position/2]).
-export([get_target/1, set_target/2]).
-export([get_up_vector/1, set_up_vector/2]).

-record(camera, {
        fov=40,
        position,
        target,
        up,
        render_size
       }).

get_ray(Camera, {X, Y}) ->
    {RW, RH} = Camera#camera.render_size,

    FOV = Camera#camera.fov,
    Position = Camera#camera.position,
    Up = Camera#camera.up,
    Target = Camera#camera.target,

    W = vector:normalize(vector:dif(Position, Target)),
    U = vector:normalize(vector:xprod(Up, vector:dif(Position, Target))),
    V = vector:xprod(U, W),

    Lu = FOV*math:pi(),
    Lv = Lu*(RH/RW),

    X1 = X+0.5-random:uniform(),
    Y1 = Y+0.5-random:uniform(),

    Cu = ((X1/RW) - 0.5)*Lu,
    Cv = ((Y1/RH) - 0.5)*Lv,

    P = vector:sum(Target, vector:sum(vector:scale(Cv, V), vector:scale(Cu, U))),
    Direction = vector:normalize(vector:dif(P, Position)),
    ray:new(Position, Direction).

new() -> #camera{}.
new(FOV, Position, Target, Up) ->
    #camera{fov=FOV, position=Position, target=Target, up=Up}.

get_render_size(Camera) -> Camera#camera.render_size.
set_render_size(Size, Camera) -> Camera#camera{render_size=Size}.

get_fov(Camera) -> Camera#camera.fov.
set_fov(FOV, Camera) -> Camera#camera{fov=FOV}.

get_position(Camera) -> Camera#camera.position.
set_position(Position, Camera) -> Camera#camera{position=Position}.

get_target(Camera) -> Camera#camera.target.
set_target(Target, Camera) -> Camera#camera{target=Target}.

get_up_vector(Camera) -> Camera#camera.up.
set_up_vector(Up, Camera) -> Camera#camera{up=Up}.
